iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
0

反射

System.Reflection 命名空間中的類別,連同 System.Type,可讓您取得已載入組件和其中所定義類型的資訊,例如類別、介面和實值型別。
您也可以使用反映在執行階段建立類型執行個體,並叫用和存取它們。

我習慣將 Reflection 叫做反射,感覺這一篇在挖坑給自己跳,這邊只會介紹動態屬性存取與調用方法。

昨天提到了 Type 類型,這個類型有一個調用成員的方法 InvokeMember。

InvokeMember

InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args)

一樣是多載方法,以下內容皆引用 Microsoft Docs 內容。

name:字串,包含要叫用的建構函式、方法、屬性或欄位成員的名稱。

invokeAttr:位元遮罩,由一或多個 BindingFlags 組成,而這些旗標會指定執行搜尋的方式。

binder:定義一組屬性並啟用繫結的物件,可包含多載方法的選擇、引數類型的強制,以及透過反映的成員引動過程。

target:要在其上叫用指定成員的物件。

args:包含引數的陣列,這些引數會傳遞給要叫用的成員。

除了 binder 都是今天會用到的引數。

從上面描述來看,只有 invokeAttr 這個引數需要多做介紹,name 就成員名稱,target 就對象物件,args 就給對應成員的引數陣列。invokeAttr 其實就是一個 enum (DAY 14 提到的列舉),透過當時講的 | 符號來疊加狀態,想了解狀態的欄位名稱與內容,就請自行前往參考連結囉。

在開始操作以前,我們需要使用System.Reflection命名空間。

這邊簡單的使用 BindingFlags.InvokeMethod、BindingFlags.SetProperty、BindingFlags.GetProperty 三種方法。

static void Main (string[] args) {
  var memberType = typeof (Member);
  var member = new Member () { ID = 1, Name = "Miffy" };

  memberType.InvokeMember ("Print", BindingFlags.InvokeMethod, null, new Member (), null);
  memberType.InvokeMember ("Print", BindingFlags.InvokeMethod, null, member, null);
  memberType.InvokeMember ("PrintWithMessage", BindingFlags.InvokeMethod, null, member, new Object[] { "s" });
  memberType.InvokeMember ("Name", BindingFlags.SetProperty, null, member, new Object[] { "Lulu" });
  var memberName = memberType.InvokeMember ("Name", BindingFlags.GetProperty, null, member, null);
}
class Member {
  public int ID { set; get; } = 0;
  public string Name { set; get; } = "Mio";
  public void Print () { Console.WriteLine ($"ID = {ID}, Name = {Name}"); }
  public void PrintWithMessage (string message) { Console.WriteLine ($"ID = {ID}, Name = {Name}, Message = {message}"); }
}

個人是滿少的情況下會去使用,只有當屬性名稱為不具有意義但有排序性的情況下(如:Time1, Time2, Time3),才會這樣動態存取,不然就是本人還沒有深到去碰觸這一塊,畢竟只是個菜鳥 QQ

在操作時也可以使用 GetProperties 傳回的 PropertyInfo 類型,並使用他的 GetValue 與 SetValue 方法如

GetValue

GetValue (object obj, object[] index);

傳回指定的物件的屬性值,和索引屬性的選擇性索引值。

obj:其屬性值將被傳回的物件。

index:索引屬性的選擇性索引值。 索引屬性的索引以零為起始。 非索引屬性的這個值應為 null。

var member = new Member () { ID = 1, Name = "Miffy" };
var memberType = typeof (Member);
var memberProperties = memberType.GetProperties ();

foreach (var property in memberProperties) {
  var value = property.GetValue (member, null);
}

SetValue

SetValue (object obj, object value)

使用索引屬性的選擇性索引值,設定指定的物件的屬性值。

obj:將設定其屬性值的物件。

value:新的屬性值。

var member = new Member () { ID = 1, Name = "Miffy" };
var memberType = typeof (Member);
var memberProperties = memberType.GetProperties ();

foreach (var property in memberProperties) {
  if (property.Name == "Name")
    property.SetValue (member, "NekoSan");
}

以及 GetMethods 傳回的 MethodInfo 類型,使用 Invoke 方法如

Invoke

Invoke (object obj, object[] parameters)

obj:要在其上叫用指定成員的物件。

parameters:包含引數的陣列,這些引數會傳遞給要叫用的成員。

var member = new Member () { ID = 1, Name = "Miffy" };
var memberType = typeof (Member);
var memberMethods = memberType.GetMethods ();

foreach (var method in memberMethods) {
  if (method.Name == "Print")
    method.Invoke (member, null);
}

一些小結論

今天簡單的介紹了反射的使用,如何動態調用成員,在使用上,往往會把 name 的字串換成變數使用,藉由變數內容來對應成員名稱,做到程式編譯時期能夠想用啥方法就用啥方法,我也是因為這原因才接觸到反射的,所以更艱深的部分我就不曉得了。

感謝閱讀,今天的內容可能有誤,閱讀後請再三求證喔。

參考連結
.NET Framework 中的反映 | Microsoft Docs
動態載入和使用類型 | Microsoft Docs
Type.InvokeMember Method (System) | Microsoft Docs
BindingFlags Enum (System.Reflection) | Microsoft Docs


上一篇
DAY 25 Type
下一篇
DAY 27 特性
系列文
.net 後端起手式 30天認識 C# 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言